﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Drawing;
using System.IO;

namespace Velocity.Ink
{
    /// <summary>
    /// Class that contains the signature data. The class knows how 
    /// to flatten itself to a stream of bytes and reconstruct 
    /// itself from a stream of bytes.
    /// </summary>
    public class SignatureData
    {
        // this identifies this class, used when sent over socket
        // so receiving application can validate the data stream
        static public string SignatureId = "signature";

        int _width;				// width of signature canvas
        int _height;			// height of signature canvas
        ArrayList _lines;		// list of line segments

        //
        // Properties
        //

        public ArrayList Lines
        {
            get { return _lines; }
        }

        public int Width
        {
            get { return _width; }
        }

        public int Height
        {
            get { return _height; }
        }

        // construct an object from stream of bytes
        public SignatureData(byte[] bits)
        {
            // index into data stream
            int bitsIndex = 0;

            // get signature id
            if (!IsValidStream(bits, ref bitsIndex))
            {
                // this is not what we are expecting
                throw (new Exception("Invalid data stream."));
            }

            // width and height
            _width = Internals.GetInt32(bits, ref bitsIndex);
            _height = Internals.GetInt32(bits, ref bitsIndex);

            // number of line segments
            Int32 linesCount = Internals.GetInt32(bits, ref bitsIndex);
            _lines = new ArrayList(linesCount);

            // loop through each line segment and get points
            for (int line = 0; line < linesCount; line++)
            {
                // number of points in this segment
                Int32 pointsCount = Internals.GetInt32(bits, ref bitsIndex);
                Point[] points = new Point[pointsCount];

                // get all points in this segment
                for (int point = 0; point < pointsCount; point++)
                {
                    points[point].X = Internals.GetInt32(bits, ref bitsIndex);
                    points[point].Y = Internals.GetInt32(bits, ref bitsIndex);
                }

                // add line segment to list
                _lines.Add(points);
            }
        }

        /// <summary>
        /// Flatten object to a stream of bytes.
        /// </summary>
        static public byte[] GetBytes(int width, int height, ArrayList lines)
        {
            // hold byte stream
            MemoryStream stream = new MemoryStream();

            // signature id
            Internals.WriteString(stream, SignatureData.SignatureId);

            // width and height
            Internals.WriteInt32(stream, width);
            Internals.WriteInt32(stream, height);

            // number of segments
            Internals.WriteInt32(stream, lines.Count);

            // each segment
            foreach (Point[] points in lines)
            {
                // points in the segment
                Internals.WriteInt32(stream, points.Length);
                foreach (Point pt in points)
                {
                    Internals.WriteInt32(stream, pt.X);
                    Internals.WriteInt32(stream, pt.Y);
                }
            }

            return stream.ToArray();
        }

        /// <summary>
        /// Return true if byte array is a valid SignatureData class,
        /// otherwise return false.
        /// </summary>
        private bool IsValidStream(byte[] bits, ref int bitsIndex)
        {
            bool valid = false;

            // see if first value is length of signature id
            Int32 idLength = Internals.GetInt32(bits, ref bitsIndex);
            if (idLength == SignatureData.SignatureId.Length)
            {
                // get the signature id mark
                byte[] id = Internals.GetBytes(bits, ref bitsIndex, idLength);

                // see if this is the stream we expect
                if (ASCIIEncoding.ASCII.GetString(id, 0, idLength) == SignatureData.SignatureId)
                    valid = true;
            }

            return valid;
        }
    }
}
